#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <stdarg.h>
#include <time.h>
#include <math.h>
#include <stdint.h>
#include <rtthread.h>
#include "sys.h"
#include "tools.h"
#include "sys_timer.h"
#include "stm32f4x7_eth.h"
#include "onps.h"

#define SYMBOL_GLOBALS
#include "emac.h"
#undef SYMBOL_GLOBALS

extern ETH_DMADESCTypeDef  DMARxDscrTab[ETH_RXBUFNB]; //* Ethernet Rx MA Descriptor
extern ETH_DMADESCTypeDef  DMATxDscrTab[ETH_TXBUFNB]; //* Ethernet Tx DMA Descriptor
extern uint8_t Rx_Buff[ETH_RXBUFNB][ETH_RX_BUF_SIZE]; //* Ethernet Receive Buffer
extern uint8_t Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE]; //* Ethernet Transmit Buffer

//* Global pointers to track current transmit and receive descriptors
extern ETH_DMADESCTypeDef  *DMATxDescToSet;
extern ETH_DMADESCTypeDef  *DMARxDescToGet;

//* Global pointer for last received frame infos
extern ETH_DMA_Rx_Frame_infos *DMA_RX_FRAME_infos;

//* mac地址
static const uint8_t lr_ubaMacAddr[ETH_MAC_ADDR_LEN] = { MAC_ADDR0, MAC_ADDR1, MAC_ADDR2, MAC_ADDR3, MAC_ADDR4, MAC_ADDR5 }; 

//* 协议栈返回的netif结构
static PST_NETIF l_pstNetifEth = NULL;  

//* ethernet网卡接收任务
#define THETHIIRECV_STK_SIZE	384 * 4
#define THETHIIRECV_TIMESLICE 10
static void start_thread_ethernet_ii_recv(void *pvParam)
{
  rt_thread_t tid = rt_thread_create("EthRcv", thread_ethernet_ii_recv, pvParam, THETHIIRECV_STK_SIZE, THETHIIRECV_PRIO, THETHIIRECV_TIMESLICE);  
  if(tid != RT_NULL)
    rt_thread_startup(tid);   
}

static void RMIIPinConfig(void)
{
  GPIO_InitTypeDef stGPIO;  
  
  //* 使能mac模块相关引脚时钟
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC, ENABLE); 
  //* 使能syscfg模块时钟
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); 
  //* phy与mac之间使用rmii接口
  SYSCFG_ETH_MediaInterfaceConfig(SYSCFG_ETH_MediaInterface_RMII);
  
  //* 配置rmii接口相关引脚
  stGPIO.GPIO_Speed = GPIO_Speed_100MHz;
  stGPIO.GPIO_Mode  = GPIO_Mode_AF;
  stGPIO.GPIO_OType = GPIO_OType_PP;
  stGPIO.GPIO_PuPd  = GPIO_PuPd_NOPULL;
  
  stGPIO.GPIO_Pin = RMII0_PIN;
  GPIO_Init(PORT_RMII0, &stGPIO);
  GPIO_PinAFConfig(PORT_RMII0, GPIO_PinSource1, GPIO_AF_ETH);
  GPIO_PinAFConfig(PORT_RMII0, GPIO_PinSource2, GPIO_AF_ETH);
  GPIO_PinAFConfig(PORT_RMII0, GPIO_PinSource7, GPIO_AF_ETH);
  
  stGPIO.GPIO_Pin = RMII1_PIN;
  GPIO_Init(PORT_RMII1, &stGPIO);
  GPIO_PinAFConfig(PORT_RMII1, GPIO_PinSource11, GPIO_AF_ETH);
  GPIO_PinAFConfig(PORT_RMII1, GPIO_PinSource12, GPIO_AF_ETH);
  GPIO_PinAFConfig(PORT_RMII1, GPIO_PinSource13, GPIO_AF_ETH);
  
  stGPIO.GPIO_Pin = RMII2_PIN;
  GPIO_Init(PORT_RMII2, &stGPIO);
  GPIO_PinAFConfig(PORT_RMII2, GPIO_PinSource1, GPIO_AF_ETH);
  GPIO_PinAFConfig(PORT_RMII2, GPIO_PinSource4, GPIO_AF_ETH);
  GPIO_PinAFConfig(PORT_RMII2, GPIO_PinSource5, GPIO_AF_ETH);
}

//* emac模块初始化
BOOL emac_init(void)
{  
  uint16_t usTimeout = 0; 
  ETH_InitTypeDef stMac; 
  
	RMIIPinConfig();   

  //* 2 bit for pre-emption priority, 2 bits for subpriority
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); 
  
  //* Enable the Ethernet global Interrupt
  NVIC_InitTypeDef stNvic;
  stNvic.NVIC_IRQChannel = ETH_IRQn;
  stNvic.NVIC_IRQChannelPreemptionPriority = 1;
  stNvic.NVIC_IRQChannelSubPriority = 0;
  stNvic.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&stNvic); 
  
  //* 使能mac相关时钟
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_ETH_MAC | RCC_AHB1Periph_ETH_MAC_Tx | RCC_AHB1Periph_ETH_MAC_Rx, ENABLE); 
  
  //* 首先去初始化mac
  ETH_DeInit(); 
  
  //* 复位mac寄存器及相关逻辑
  ETH_SoftwareReset(); 
  
  //* 等待mac复位结束
  while (ETH_GetSoftwareResetStatus() == SET)
  {    
    DelaymsByCount(1);
    
    usTimeout++;
    //* 超过一定时间则认为初始化失败，外部晶振有问题？
    if(usTimeout >= 1000)
      return FALSE;
  }
  
  //* 开始设置，首先初始化配置结构体，这样我们只需关注要调整的字段即可
  ETH_StructInit(&stMac); 
  
  //* 填充结构体要设置的相关字段
  //* ------------------------ MAC -----------------------------------
  stMac.ETH_AutoNegotiation          = ETH_AutoNegotiation_Enable; //* 10M/100M自适应
  stMac.ETH_LoopbackMode             = ETH_LoopbackMode_Disable;
  stMac.ETH_RetryTransmission        = ETH_RetryTransmission_Disable;
  stMac.ETH_AutomaticPadCRCStrip     = ETH_AutomaticPadCRCStrip_Disable;
  //stMac.ETH_ReceiveAll               = ETH_ReceiveAll_Enable; //* 接收所有到达的帧，无论其是否已通过地址过滤，此时PromiscuousMode被禁止时，SA/DA仍然会将所有帧传送给上层应用，只不过SA/DA位会指示当前帧是否已通过地址过滤；PromiscuousMode使能则该位将被清零，无法指示
  stMac.ETH_ReceiveAll               = ETH_ReceiveAll_Disable;
  stMac.ETH_BroadcastFramesReception = ETH_BroadcastFramesReception_Enable;
  stMac.ETH_PromiscuousMode          = ETH_PromiscuousMode_Disable; //* 禁止混杂模式，如此则地址过滤器将会指示当前到达的帧是否已通过地址过滤（否则SA/DA位会被清零）
  stMac.ETH_MulticastFramesFilter    = ETH_MulticastFramesFilter_Perfect;
  stMac.ETH_UnicastFramesFilter      = ETH_UnicastFramesFilter_Perfect;
  stMac.ETH_ChecksumOffload          = ETH_ChecksumOffload_Enable; //* 在IP包收发时使用硬件校验和
  
  //*------------------------   DMA   -----------------------------------  
  /* When we use the Checksum offload feature, we need to enable the Store and Forward mode: 
  the store and forward guarantee that a whole frame is stored in the FIFO, so the MAC can insert/verify the checksum, 
  if the checksum is OK the DMA can handle the frame otherwise the frame is dropped */
  stMac.ETH_DropTCPIPChecksumErrorFrame = ETH_DropTCPIPChecksumErrorFrame_Enable; 
  stMac.ETH_ReceiveStoreForward         = ETH_ReceiveStoreForward_Enable;         
  stMac.ETH_TransmitStoreForward        = ETH_TransmitStoreForward_Enable;      
  stMac.ETH_ForwardErrorFrames          = ETH_ForwardErrorFrames_Disable;       
  stMac.ETH_ForwardUndersizedGoodFrames = ETH_ForwardUndersizedGoodFrames_Disable;   
  stMac.ETH_SecondFrameOperate          = ETH_SecondFrameOperate_Enable;
  stMac.ETH_AddressAlignedBeats         = ETH_AddressAlignedBeats_Enable;      
  stMac.ETH_FixedBurst                  = ETH_FixedBurst_Enable;                
  stMac.ETH_RxDMABurstLength            = ETH_RxDMABurstLength_32Beat;          
  stMac.ETH_TxDMABurstLength            = ETH_TxDMABurstLength_32Beat;
  stMac.ETH_DMAArbitration              = ETH_DMAArbitration_RoundRobin_RxTx_2_1;

  //* Configure Ethernet
  while(ETH_ERROR == ETH_Init(&stMac, DP83848_PHY_ADDRESS))
    os_sleep_ms(10);  
  
  //* Enable the Ethernet Rx Interrupt
  ETH_DMAITConfig(ETH_DMA_IT_NIS | ETH_DMA_IT_R, ENABLE);   
  
  ETH_MACAddressConfig(ETH_MAC_Address0, lr_ubaMacAddr); 
  
  //* Initialize Tx Descriptors list: Chain Mode
  ETH_DMATxDescChainInit(DMATxDscrTab, &Tx_Buff[0][0], ETH_TXBUFNB);
  //* Initialize Rx Descriptors list: Chain Mode
  ETH_DMARxDescChainInit(DMARxDscrTab, &Rx_Buff[0][0], ETH_RXBUFNB);
  
  //* 发送报文的校验和插入操作旁路
  INT i; 
  for(i=0; i<ETH_TXBUFNB; i++)  
    ETH_DMATxDescChecksumInsertionConfig(&DMATxDscrTab[i], ETH_DMATxDesc_ChecksumByPass/* ETH_DMATxDesc_ChecksumTCPUDPICMPFull */);  
  
  //* ethernet启动之前先将其加入协议栈    
  EN_ONPSERR enErr; 
  ST_IPV4 stIPv4; 
#if !DHCP_REQ_ADDR_EN  
  stIPv4.unAddr = inet_addr_small("192.168.0.4"); 
  stIPv4.unSubnetMask = inet_addr_small("255.255.255.0"); 
  stIPv4.unGateway = inet_addr_small("192.168.0.1"); 
  stIPv4.unPrimaryDNS = inet_addr_small("1.2.4.8"); 
  stIPv4.unSecondaryDNS = inet_addr_small("8.8.8.8"); 
  stIPv4.unBroadcast = inet_addr_small("192.168.0.255"); 
#else
  memset(&stIPv4, 0, sizeof(stIPv4)); 
#endif  
  l_pstNetifEth = ethernet_add(NETIF_ETH_NAME, lr_ubaMacAddr, &stIPv4, emac_send, start_thread_ethernet_ii_recv, &l_pstNetifEth, &enErr); 
  if(!l_pstNetifEth)
  {
#if SUPPORT_PRINTF    
    printf("ethernet_add() failed, %s\r\n", onps_error(enErr)); 
#endif    
    return FALSE; 
  }
  
  //* Enable MAC and DMA transmission and reception
  ETH_Start();
  
#if DHCP_REQ_ADDR_EN  
  if(dhcp_req_addr(l_pstNetifEth, &enErr))  
  {
#if SUPPORT_PRINTF     
    printf("dhcp request ip address successfully.\r\n"); 
#endif    
  }
  else
  {
#if SUPPORT_PRINTF     
    printf("dhcp request ip address failed, %s\r\n", onps_error(enErr)); 
#endif    
  }
#endif  
  
  return TRUE; 
}

void ETH_IRQHandler(void)
{
  __IO ETH_DMADESCTypeDef *DMARxNextDesc;  
  UCHAR *pubPacket; 
  EN_ONPSERR enErr; 
  
  rt_interrupt_enter(); //* 进入中断		
  {
    //* 处理所有到达的报文
    while(ETH_CheckFrameReceived())
    { 
      //* 获取到达的一帧完整的ethernet ii协议数据
      FrameTypeDef stRcvedFrame = ETH_Get_Received_Frame(); 
      
      pubPacket = (UCHAR *)buddy_alloc(sizeof(ST_SLINKEDLIST_NODE) + stRcvedFrame.length, &enErr); 
      if(pubPacket)
      {
        //* 搬运数据到接收链表
        PST_SLINKEDLIST_NODE pstNode = (PST_SLINKEDLIST_NODE)pubPacket; 
        pstNode->uniData.unVal = stRcvedFrame.length; 
        memcpy(pubPacket + sizeof(ST_SLINKEDLIST_NODE), (UCHAR *)stRcvedFrame.buffer, stRcvedFrame.length); 
        ethernet_put_packet(l_pstNetifEth, pstNode); 
      }
      else
      {
    #if SUPPORT_PRINTF && DEBUG_LEVEL        
        printf("<EIRQ> %s\r\n", onps_error(enErr));
    #endif
      }
      
      //* 收到的报文传递给协议栈提供的ethernet ii层协议解析函数处理
      //ethernet_ii_recv(l_pstNetifEth, (UCHAR *)stRcvedFrame.buffer, (INT)stRcvedFrame.length); 
      
      //* Release descriptors to DMA 
      //* Check if frame with multiple DMA buffer segments
      if (DMA_RX_FRAME_infos->Seg_Count > 1)
      {
        DMARxNextDesc = DMA_RX_FRAME_infos->FS_Rx_Desc;
      }
      else
      {
        DMARxNextDesc = stRcvedFrame.descriptor;
      }
      
      //* Set Own bit in Rx descriptors: gives the buffers back to DMA
      for (int i=0; i<DMA_RX_FRAME_infos->Seg_Count; i++)
      {  
        DMARxNextDesc->Status = ETH_DMARxDesc_OWN;
        DMARxNextDesc = (ETH_DMADESCTypeDef *)(DMARxNextDesc->Buffer2NextDescAddr);
      }
      
      //* Clear Segment_Count
      DMA_RX_FRAME_infos->Seg_Count = 0;
      
      //* When Rx Buffer unavailable flag is set: clear it and resume reception
      if ((ETH->DMASR & ETH_DMASR_RBUS) != (u32)RESET)  
      {
        //* Clear RBUS ETHERNET DMA flag
        ETH->DMASR = ETH_DMASR_RBUS;
        //* Resume DMA reception
        ETH->DMARPDR = 0;
      }
    }
    
    //* Clear the Eth DMA Rx IT pending bits
    ETH_DMAClearITPendingBit(ETH_DMA_IT_R);
    ETH_DMAClearITPendingBit(ETH_DMA_IT_NIS);
  }
  rt_interrupt_leave(); //* 离开中断
}

INT emac_send(SHORT sBufListHead, UCHAR *pubErr)
{  
  SHORT sNextNode = sBufListHead; 
  UCHAR *pubData;
  USHORT usDataLen; 
  UCHAR *pubDMATxBuf = (UCHAR *)DMATxDescToSet->Buffer1Addr; 
  __IO ETH_DMADESCTypeDef *DMATxNextDesc = DMATxDescToSet; 
  UINT unHasCpyBytes = 0, unRemainBytes, unCpyBytes; 
  
  //* 首先看看报文长度是否超出了DMA发送缓冲区的限制
  UINT unEthPacketLen = buf_list_get_len(sBufListHead); 
  if(unEthPacketLen > ETH_TX_BUF_SIZE * ETH_TXBUFNB) 
  {
    if(pubErr)
      *((EN_ONPSERR *)pubErr) = ERRPACKETTOOLARGE; 
    return -1; 
  }
  
  //* 遍历所有节点并将它们逐个放入DMA缓冲区    
__lblGetNextNode: 
  pubData = (UCHAR *)buf_list_get_next_node(&sNextNode, &usDataLen);
  if (NULL == pubData) //* 取出完毕，启动DMA发送
  {
    ETH_Prepare_Transmit_Descriptors((USHORT)unEthPacketLen); 
    return (INT)unEthPacketLen; 
  }  
  unRemainBytes = (UINT)usDataLen; 
  
__lblCpy:   
  //* 当前DMA缓冲区链表节点是否已满，如果已满则移动到下一个节点继续接收
  if(unHasCpyBytes >= ETH_TX_BUF_SIZE)
  {
    //* 指向环形链表的下一个节点，不用考虑节点是否为空的问题，因为这个链表是一个全封闭的环形链表
    DMATxNextDesc = (ETH_DMADESCTypeDef *)(DMATxNextDesc->Buffer2NextDescAddr);     
    pubDMATxBuf = (UCHAR *)DMATxNextDesc->Buffer1Addr; 
    unHasCpyBytes = 0; 
  }  
   
  //* 计算能够copy的数据长度
  unCpyBytes = unRemainBytes < (ETH_TX_BUF_SIZE - unHasCpyBytes) ? unRemainBytes : (ETH_TX_BUF_SIZE - unHasCpyBytes); 
  memcpy(pubDMATxBuf + unHasCpyBytes, pubData + (usDataLen - unRemainBytes), unCpyBytes); 
  unRemainBytes -= unCpyBytes; 
  unHasCpyBytes += unCpyBytes;
  if(!unRemainBytes)
    goto __lblGetNextNode;  
  else
    goto __lblCpy; 
}
